﻿using System.Collections.Generic;
using Framework;
using UnityEngine;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.SceneManagement;

namespace Game
{
    public class SceneEntity : CommonEntity
    {
        public SceneInstance sceneInstance { get; private set; }

        public string name
        {
            get
            {
                return _sceneData.name;
            }
        }
        
        public bool isActive { get; private set; }

        private SceneConfigData _sceneData;
        private GameObject _rootGameObject;
        private Transform _rootTransform;
        private SceneLayerEntity _backgroundLayer;
        private SceneLayerEntity _sceneObjectLayer;
        private SceneLayerEntity _foregroundLayer;
        private SceneCameraComponent _cameraComponent;
        private SceneControlComponent _controlComponent;
        private SceneSharedPropertiesComponent _sharePropertiesComponent;
        private SceneObjectFinderComponent _objectFinderComponent;
        private int _sceneObjectId = 1;

        public SceneEntity(SceneInstance sceneInstance, SceneConfigData sceneData)
        {
            this.sceneInstance = sceneInstance;
            this._sceneData = sceneData;
            _rootGameObject = sceneInstance.Scene.GetRootGameObjects()[0];
            _rootTransform = _rootGameObject.transform;
            this.isActive = false;
            InitSharedComponents();
            InitComponents();
            InitLayers();
            InitMonsters();
            InitPlayerAndPet();
            
            _backgroundLayer.SetTrackCamera(_sharePropertiesComponent.camera.transform, 
                _sharePropertiesComponent.sceneBounds);
            
            Scheduler.Instance.AddFrameListener(1, Update);
        }

        public override void Dispose()
        {
            Scheduler.Instance.RemoveFrameListener(Update);
            _backgroundLayer.Dispose();
            _backgroundLayer = null;
            _sceneObjectLayer.Dispose();
            _sceneObjectLayer = null;
            _foregroundLayer.Dispose();
            _foregroundLayer = null;
            base.Dispose();
        }

        public void SetActive(bool isActive)
        {
            if (this.isActive != isActive)
            {
                this.isActive = isActive;
            }
        }

        private void InitSharedComponents()
        {
            _sharePropertiesComponent = AddComponent<SceneSharedPropertiesComponent>();
            BoxCollider2D sizeCollider = _rootTransform.Find("Size").GetComponent<BoxCollider2D>();
            Bounds sceneBounds = sizeCollider.bounds;
            sizeCollider.enabled = false;
            _sharePropertiesComponent.SetSceneBounds(sceneBounds);
            _sharePropertiesComponent.SetCamera(Camera.main);

            _objectFinderComponent = AddComponent<SceneObjectFinderComponent>();
        }

        private void InitComponents()
        {
            _cameraComponent = AddComponent<SceneCameraComponent>();
            _cameraComponent.Setup(_sharePropertiesComponent.camera, _sharePropertiesComponent.sceneBounds);
            _controlComponent = AddComponent<SceneControlComponent>();
        }

        private void InitLayers()
        {
            int order = 0, childCount = 0;
            #region Backgrounds
            
            Transform backgroundsTransform = _rootTransform.Find("Backgrounds");
            _backgroundLayer = new SceneLayerEntity(backgroundsTransform);
            _backgroundLayer.SetLayerOrder(order);
            childCount = backgroundsTransform.childCount;
            for (int i = 0; i < childCount; ++i)
            {
                Transform childTransform = backgroundsTransform.GetChild(i);
                SceneSpriteGroupEntity spriteGroupEntity = CreateSpriteGroupEntity(childTransform);
                _backgroundLayer.AddSceneObject(spriteGroupEntity);
            }
            
            #endregion

            #region SceneObjects
            
            order += SceneConst.SceneLayerOrderInterval;
            _sceneObjectLayer = new SceneLayerEntity(_rootTransform.Find("SceneObjects"));
            _sceneObjectLayer.SetLayerOrder(order);
            #endregion

            #region Foregrounds
            
            order += SceneConst.SceneLayerOrderInterval;
            Transform foregroundsTransform = _rootTransform.Find("Foregrounds");
            _foregroundLayer = new SceneLayerEntity(foregroundsTransform);
            _foregroundLayer.SetLayerOrder(order);
            childCount = foregroundsTransform.childCount;
            for (int i = 0; i < childCount; ++i)
            {
                Transform childTransform = foregroundsTransform.GetChild(i);
                SceneSpriteGroupEntity spriteGroupEntity = CreateSpriteGroupEntity(childTransform);
                _backgroundLayer.AddSceneObject(spriteGroupEntity);
            }
            
            #endregion
        }

        private void InitPlayerAndPet()
        {
            Vector3 playerPosition = new Vector3(2f, 0f, 0f);
            Vector3 petPosition = new Vector3(3f, 0f, 0f);
            
            ScenePetEntity petEntity = CreatePetEntity(10001);
            _sceneObjectLayer.AddSceneObject(petEntity);
            petEntity.localPosition = petPosition;
            
            _cameraComponent.SetTrackEntity(petEntity);
            _controlComponent.SetControlEntity(petEntity);

            ScenePlayerEntity playerEntity = CreatePlayerEntity(30001);
            _sceneObjectLayer.AddSceneObject(playerEntity);
            playerEntity.SetPet(petEntity);
            playerEntity.localPosition = playerPosition;
        }

        private void InitMonsters()
        {
            List<Vector3> monsterPosList = new List<Vector3>()
            {
                new Vector3(8f, 0f, 0f), new Vector3(12f, 0f, 0f),
                new Vector3(16f, 0f, 0f), new Vector3(20f, 0f, 0f)
            };
            for (int i = 0; i < monsterPosList.Count; ++i)
            {
                Vector3 localPosition = monsterPosList[i];
                SceneMonsterEntity monsterEntity = CreateMonsterEntity(10002);
                _sceneObjectLayer.AddSceneObject(monsterEntity);
                monsterEntity.localPosition = localPosition;
                monsterEntity.SetFlipY(true);
            }
        }

        private ScenePlayerEntity CreatePlayerEntity(int playerConfigId)
        {
            ScenePlayerEntity playerEntity = new ScenePlayerEntity(GenerateSceneObjectId());
            playerEntity.AddSharedComponent(_sharePropertiesComponent);
            playerEntity.LoadPlayer(playerConfigId);
            return playerEntity;
        }
        
        private ScenePetEntity CreatePetEntity(int petConfigId)
        {
            ScenePetEntity petEntity = new ScenePetEntity(GenerateSceneObjectId());
            petEntity.AddSharedComponent(_sharePropertiesComponent);
            petEntity.AddSharedComponent(_objectFinderComponent);
            petEntity.LoadPet(petConfigId);
            _objectFinderComponent.AddPet(petEntity);
            return petEntity;
        }
        
        private SceneMonsterEntity CreateMonsterEntity(int monsterConfigId)
        {
            SceneMonsterEntity monsterEntity = new SceneMonsterEntity(GenerateSceneObjectId());
            monsterEntity.AddSharedComponent(_sharePropertiesComponent);
            monsterEntity.AddSharedComponent(_objectFinderComponent);
            monsterEntity.LoadMonster(monsterConfigId);
            _objectFinderComponent.AddMonster(monsterEntity);
            return monsterEntity;
        }

        private SceneSpriteGroupEntity CreateSpriteGroupEntity(Transform spriteTransform)
        {
            SceneSpriteGroupEntity spriteGroupEntity = new SceneSpriteGroupEntity(GenerateSceneObjectId(), spriteTransform);
            return spriteGroupEntity;
        }
        
        private int GenerateSceneObjectId()
        {
            int id = _sceneObjectId;
            ++_sceneObjectId;
            return id;
        }

        private void Update(float deltaTime)
        {
            if (isActive)
            {
                _backgroundLayer.Update(deltaTime);
                _sceneObjectLayer.Update(deltaTime);
                _foregroundLayer.Update(deltaTime);
                for (int i = 0; i < _componentList.Count; ++i)
                {
                    IComponent component = _componentList[i];
                    if (component is ISceneComponent)
                    {
                        (component as ISceneComponent).Update(deltaTime);
                    }
                }
            }
        }
    }
}